#ifndef __CHANNEL_H__
#define __CHANNEL_H__

#include <functional>
#include <memory>
#include <map>
#include <poll.h>

enum class CBType : int
{
    READ = POLLIN | POLLPRI | POLLRDHUP,
    WRITE = POLLOUT,
    CLOSE = POLLHUP,
    ERROR = POLLNVAL | POLLERR,
    EVENT = 0x7FFF
};

enum class Event : int
{
    kNonEvent = 0,
    kReadEvent = POLLIN | POLLPRI,
    kWriteEvent = POLLOUT
};

inline int operator&(CBType t, int i)
{
    return static_cast<int>(t) & i;
}

inline int operator&(int i, Event e)
{
    return i & static_cast<int>(e);
}

inline void operator|=(int& i, Event e)
{
    i |= static_cast<int>(e);
}

inline int operator~(Event e)
{
    return ~static_cast<int>(e);
}

class EventLoop;
class Channel
{
    using EventCallback = std::function<void()>;

  public:
    Channel(EventLoop* loop, int fd);
    ~Channel();

  public:
    enum class Status : int
    {
        kNew,
        kAdded,
        kDeleted
    };

    /**
     * @brief Set the Callback object according to CBType enum class
     * @param  t Callback types: READ, WRITE, CLOSE, ERROR, EVENT
     * @param  cb EventCallback object rvalue
     */
    void setCallback(CBType t, EventCallback cb);

    /**
     * @brief Disable all watching events
     */
    void disableAll();

    /**
     * @brief Remove this Channel from epoll watch list
     */
    void remove();

    /**
     * @brief Return the owner EventLoop pointer
     * @return EventLoop*
     */
    EventLoop* ownerLoop()
    {
        return loop_;
    }

    /**
     * @brief Return the events' mask set to epoll
     * @return int
     */
    int eventMask() const
    {
        return eventMask_;
    }

    /**
     * @brief Return the occurred events' mask
     * @return int
     */
    int occurred() const
    {
        return occurred_;
    }

    /**
     * @brief Return managing file descriptor
     * @return int
     */
    int fd() const
    {
        return FD_;
    }

    /**
     * @brief Set the Status of Channel
     * @param  s XXX
     */
    void setStatus(Status s)
    {
        status_ = s;
    }

    /**
     * @brief Set epoll to watch readable events
     */
    void addReadEvent();

    /**
     * @brief Set epoll to watch writable events
     */
    void addWriteEvent();

    /**
     * @brief Set epoll to unwatch readable events
     */
    void removeReadEvent();

    /**
     * @brief set epoll to unwatch writable events
     */
    void removeWriteEvent();

    /**
     * @brief Check if Channel is watching readable events
     * @return true
     * @return false
     */
    bool isReadEvent() const;

    /**
     * @brief Check if Channel is watching writable events
     * @return true
     * @return false
     */
    bool isWriteEvent() const;

    /**
     * @brief Check if Channel doesn't have event
     * @return true
     * @return false
     */
    bool isNonEvent() const;

    /**
     * @brief Update eventfd with given events mask
     * @param  eventMask XXX
     */
    void updateEventMask(int eventMask);

    /**
     * @brief Ties a weak_ptr with the owner fo Callback object
     * in case handling the callback when the owner has been destructed
     * @param  cbOwner XXX
     */
    void tieCallbackOwner(const std::shared_ptr<void>& cbOwner);

    /**
     * @brief Set the Occurred event mask
     * @param  eventMask
     */
    void setOccurred(int eventMask)

    {
        occurred_ = eventMask;
    }

    Status status() const
    {
        return status_;
    }

    /**
     * @brief Wrapper function to check if Channel tied with weak object
     */
    void handleEvent();

  private:
    /**
     * @brief Really handling the event
     */
    void handleEventWithGuard();

    /**
     * @brief Update epoll with Channel's eventMask_
     */
    void update();

  private:
    EventLoop* loop_;
    int FD_;
    int eventMask_{0};
    int occurred_{0};
    Status status_{Status::kAdded};
    bool tied_{false};
    std::weak_ptr<void> weakTie_;
    EventCallback readEventCallback_;
    EventCallback writeEventCallback_;
    EventCallback closeEventCallback_;
    EventCallback errorEventCallback_;
    EventCallback allEventCallback_;
};

#endif  // __CHANNEL_H__